/*****************************************************************************
*
* Copyright (c) 2000 - 2012, Lawrence Livermore National Security, LLC
* Produced at the Lawrence Livermore National Laboratory
* LLNL-CODE-442911
* All rights reserved.
*
* This file is  part of VisIt. For  details, see https://visit.llnl.gov/.  The
* full copyright notice is contained in the file COPYRIGHT located at the root
* of the VisIt distribution or at http://www.llnl.gov/visit/copyright.html.
*
* Redistribution  and  use  in  source  and  binary  forms,  with  or  without
* modification, are permitted provided that the following conditions are met:
*
*  - Redistributions of  source code must  retain the above  copyright notice,
*    this list of conditions and the disclaimer below.
*  - Redistributions in binary form must reproduce the above copyright notice,
*    this  list of  conditions  and  the  disclaimer (as noted below)  in  the
*    documentation and/or other materials provided with the distribution.
*  - Neither the name of  the LLNS/LLNL nor the names of  its contributors may
*    be used to endorse or promote products derived from this software without
*    specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT  HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT  LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS FOR A PARTICULAR  PURPOSE
* ARE  DISCLAIMED. IN  NO EVENT  SHALL LAWRENCE  LIVERMORE NATIONAL  SECURITY,
* LLC, THE  U.S.  DEPARTMENT OF  ENERGY  OR  CONTRIBUTORS BE  LIABLE  FOR  ANY
* DIRECT,  INDIRECT,   INCIDENTAL,   SPECIAL,   EXEMPLARY,  OR   CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT  LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR
* SERVICES; LOSS OF  USE, DATA, OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER
* CAUSED  AND  ON  ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT
* LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY  WAY
* OUT OF THE  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*****************************************************************************/

// ************************************************************************* //
//                            avtgr3FileFormat.C                           //
// ************************************************************************* //

#include <avtgr3FileFormat.h>

#include <vtkFloatArray.h>
#include <vtkDoubleArray.h>
#include <vtkRectilinearGrid.h>
#include <vtkStructuredGrid.h>
#include <vtkUnstructuredGrid.h>

#include <vtkPoints.h>
#include <vtkCellType.h> 

#include <avtDatabaseMetaData.h>
#include <avtVariableCache.h>

#include <DBOptionsAttributes.h>
#include <Expression.h>

#include <InvalidVariableException.h>
#include <InvalidDBTypeException.h>
#include <InvalidTimeStepException.h>
#include <InvalidFilesException.h>
#include <DBYieldedNoDataException.h>
#include <DebugStream.h>
#include <iostream>
#include <fstream>  
#include <sstream> 
using std::ios;
using std::ifstream;

const std::string HGRID="hgrid.gr3";
const std::string GR3MESH2D ="HGRID_GR3_MESH";
const int         MAXIMUMNODEPERCELL = 4;
// ****************************************************************************
//  Method: avtgr3FileFormat constructor
//
//  Programmer: qshu -- generated by xml2avt
//  Creation:   Tue Apr 16 09:35:00 PDT 2013
//
// ****************************************************************************

avtgr3FileFormat::avtgr3FileFormat(const char *filename)
    : avtSTSDFileFormat(filename),
      m_gr3File(filename),
      m_mesh2DName("2D_Mesh_")
{
	std::string gr3File(filename);
	size_t startPos       = gr3File.find_last_of(".");
    std::string suffix;
	
    if (!(startPos == std::string::npos))
    {
       suffix  = gr3File.substr(startPos+1,3);
    }

	if(!(suffix=="gr3"))
    { 
		EXCEPTION1(InvalidDBTypeException,"This file cann't be opend as gr3 file\n");
	}

	try
	{
         loadMeshandVars();
	}
	catch(...)
	{
		EXCEPTION1(InvalidDBTypeException,"This file cann't be opend as gr3 file\n")
	}
}


// ****************************************************************************
//  Method: avtgr3FileFormat::FreeUpResources
//
//  Purpose:
//      When VisIt is done focusing on a particular timestep, it asks that
//      timestep to free up any resources (memory, file descriptors) that
//      it has associated with it.  This method is the mechanism for doing
//      that.
//
//  Programmer: qshu -- generated by xml2avt
//  Creation:   Tue Apr 16 09:35:00 PDT 2013
//
// ****************************************************************************

void
avtgr3FileFormat::FreeUpResources(void)
{
   if (m_nodeXPtr)
     {
        delete m_nodeXPtr;
     }
   if (m_nodeYPtr)
     {
        delete m_nodeYPtr;
     }
   if (m_varPtr)
     {
        delete m_varPtr;
     }
   if(m_faceNodesPtr)
     {
        delete m_faceNodesPtr;
     }
}


// ****************************************************************************
//  Method: avtgr3FileFormat::PopulateDatabaseMetaData
//
//  Purpose:
//      This database meta-data object is like a table of contents for the
//      file.  By populating it, you are telling the rest of VisIt what
//      information it can request from you.
//
//  Programmer: qshu -- generated by xml2avt
//  Creation:   Tue Apr 16 09:35:00 PDT 2013
//
// ****************************************************************************

void
avtgr3FileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    //
    // CODE TO ADD A MESH
    
    string meshname = "MESH_2D";

    //
    // AVT_RECTILINEAR_MESH, AVT_CURVILINEAR_MESH, AVT_UNSTRUCTURED_MESH,
    // AVT_POINT_MESH, AVT_SURFACE_MESH, AVT_UNKNOWN_MESH
    avtMeshType mt = AVT_UNSTRUCTURED_MESH;
    //
    int nblocks = 1;  
    int block_origin = 0;
    int spatial_dimension = 2;
    int topological_dimension = 2;
    double *extents = NULL;
    

    // Here's the call that tells the meta-data object that we have a mesh:
    //
    AddMeshToMetaData(md, meshname, mt, extents, nblocks, block_origin,
                       spatial_dimension, topological_dimension);
    

    //
    // CODE TO ADD A SCALAR VARIABLE
    //
    string mesh_for_this_var = meshname; 
    string varname = m_variableName;

    //
    // AVT_NODECENT, AVT_ZONECENT, AVT_UNKNOWN_CENT
     avtCentering cent = AVT_NODECENT;
    //
    //
    // Here's the call that tells the meta-data object that we have a var
     AddScalarVarToMetaData(md, varname, mesh_for_this_var, cent);

   
}


// ****************************************************************************
//  Method: avtgr3FileFormat::GetMesh
//
//  Purpose:
//      Gets the mesh associated with this file.  The mesh is returned as a
//      derived type of vtkDataSet (ie vtkRectilinearGrid, vtkStructuredGrid,
//      vtkUnstructuredGrid, etc).
//
//  Arguments:
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: qshu -- generated by xml2avt
//  Creation:   Tue Apr 16 09:35:00 PDT 2013
//
// ****************************************************************************

vtkDataSet *
avtgr3FileFormat::GetMesh(const char *meshname)
{
     int   nDims              = 3;
     int   numNodes           = m_numMeshNodes;
     int   numCells           = m_numMeshFaces;

     int   domainID           = 0;
     int   timeState          = 0;
     std::string material("all");
     std::string cacheMeshID(meshname);
     cacheMeshID             += m_gr3File;  
     
     vtkObject * cachedMesh   = cache->GetVTKObject(cacheMeshID.c_str(),
                                                    avtVariableCache::DATASET_NAME,
                                                    timeState, 
                                                    domainID, 
                                                    material.c_str());

 
     if(cachedMesh!=NULL)
     {
      vtkUnstructuredGrid *uGrid = (vtkUnstructuredGrid *)cachedMesh;
      uGrid->Register(NULL);
      return uGrid;
      }

      debug1<<"start creating mesh\n";

      vtkUnstructuredGrid *uGrid = vtkUnstructuredGrid::New();

      vtkPoints *points      = vtkPoints::New();
      points->SetNumberOfPoints(numNodes);
      float * pointPtr       = (float *) points->GetVoidPointer(0);
        
      for(int iNode=0;iNode < numNodes; iNode++)
      {
             
        float x            = m_nodeXPtr[iNode];
        float y            = m_nodeYPtr[iNode];
        *pointPtr++        = x;
        *pointPtr++        = y;
        // must put a dummy z value as visit manaul example does
         *pointPtr++       = 0.0;
        debug1<<iNode<<" "<<x<<" "<<y<<endl; 
       }
       uGrid ->SetPoints(points);
       points->Delete();
       uGrid ->Allocate( m_numMeshFaces);
       debug1<<" num faces: "<<m_numMeshFaces<<endl;
       int * nodesPtrTemp = m_faceNodesPtr;
       for(int iCell = 0; iCell < m_numMeshFaces; ++iCell)
       {
		 int num_nodes = nodesPtrTemp[0];
		 
		 if (num_nodes ==3)
		 {
            vtkIdType verts[3];
            for(int iNode=1;iNode<4;++iNode)
            {
               verts[iNode-1] = nodesPtrTemp[iNode]-1;
            } 
            uGrid->InsertNextCell(VTK_TRIANGLE, 3, verts);
		 }
		 else if (num_nodes ==4 )
		 {
            vtkIdType verts[4];
            for(int iNode=1;iNode<5;++iNode)
            {
               verts[iNode-1] = nodesPtrTemp[iNode]-1;
            } 
            uGrid->InsertNextCell(VTK_QUAD, 4, verts);

		 }
		 else
		 {
			 stringstream msgStream(stringstream::out);
             msgStream <<"invalid cell type with number of nodes: " <<num_nodes;
			throw InvalidVariableException(msgStream.str());
		 }
		 
		 nodesPtrTemp += (MAXIMUMNODEPERCELL+1);
       }
    
      size_t found = m_gr3File.find(HGRID);
      //special name for hgrid.gr3 mesh
      if (found!=std::string::npos)
      {
        cache->CacheVTKObject(GR3MESH2D.c_str(), 
                              avtVariableCache::DATASET_NAME, 
                              timeState, 
                              domainID,
                              material.c_str(), 
                              uGrid); 
        debug1<<GR3MESH2D<<" cached \n";
       }
      else
       {
          cache->CacheVTKObject(cacheMeshID.c_str(), 
                                avtVariableCache::DATASET_NAME, 
                                timeState, 
                                domainID,
                                material.c_str(), 
                                uGrid); 
       }
     return uGrid;

}


// ****************************************************************************
//  Method: avtgr3FileFormat::GetVar
//
//  Purpose:
//      Gets a scalar variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      varname    The name of the variable requested.
//
//  Programmer: qshu -- generated by xml2avt
//  Creation:   Tue Apr 16 09:35:00 PDT 2013
//
// ****************************************************************************

vtkDataArray *
avtgr3FileFormat::GetVar(const char *varname)
{
  
     int ntuples        = m_numMeshNodes; 
     vtkDoubleArray *rv = vtkDoubleArray::New();
     rv->SetNumberOfTuples(ntuples);
     int idata = 0;    
     for( int iNode = 0 ; iNode < m_numMeshNodes; iNode++)
       {
         float valTemp = m_varPtr[iNode];
         rv->SetTuple1(idata, valTemp);  
         idata++;             
       }
     return rv;

   
}


// ****************************************************************************
//  Method: avtgr3FileFormat::GetVectorVar
//
//  Purpose:
//      Gets a vector variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      varname    The name of the variable requested.
//
//  Programmer: qshu -- generated by xml2avt
//  Creation:   Tue Apr 16 09:35:00 PDT 2013
//
// ****************************************************************************

vtkDataArray *
avtgr3FileFormat::GetVectorVar(const char *varname)
{
    
  return 0;
  
}


void avtgr3FileFormat::loadMeshandVars()
{

   debug1<<"start loading xy\n";

   ifstream*    gr3FileStream = new ifstream(m_gr3File.c_str()); 
   if (!gr3FileStream->good())
   {
       EXCEPTION1(InvalidDBTypeException,"This file cann't be opend as gr3 file\n")
   }
    std::string  lineTemp;
  
    std::getline(*gr3FileStream,lineTemp);
    std::stringstream headstream(lineTemp);
    headstream>>m_variableName;

	std::getline(*gr3FileStream,lineTemp);
	std::stringstream sizeStream(lineTemp);
	sizeStream>>m_numMeshFaces>>m_numMeshNodes;
	int stepsize =  MAXIMUMNODEPERCELL+1;
	m_nodeXPtr     = new float [m_numMeshNodes];
	m_nodeYPtr     = new float [m_numMeshNodes];
	m_faceNodesPtr = new int   [stepsize*m_numMeshFaces];
	m_varPtr       = new float [m_numMeshNodes];

	for(int iNode=0;iNode<m_numMeshNodes;iNode++)
		{
		int valtemp;
		std::getline(*gr3FileStream,lineTemp);
		std::stringstream nodeStream(lineTemp);
		nodeStream>>valtemp>>m_nodeXPtr[iNode]>>m_nodeYPtr[iNode]>>m_varPtr[iNode];
		}
   

	for(int iFace=0;iFace<m_numMeshFaces;iFace++)
		{
		int valtemp;
		int numNode;
		std::getline(*gr3FileStream,lineTemp);
		std::stringstream faceStream(lineTemp);
		faceStream>>valtemp>>numNode;
		m_faceNodesPtr[iFace*stepsize] = numNode;
	  
		for(int iNode=0;iNode<numNode;iNode++)
			{
			faceStream>>m_faceNodesPtr[iFace*stepsize+iNode+1];
			}
		}
   
	debug1<<"finish load data\n";
	gr3FileStream->close();
	delete gr3FileStream;
  

   
}
